package base;

import java.io.Serializable;




public class Arc implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -4439194811876389888L;
	/**
	 * Assumptions: 
	 *  0 <= angle < 360 (Geometry.ensurePositive can be used to make sure angles are between these limits)
	 *  
	 * No assumptions regarding the order of the begin and end, i.e. you can create arc from 45 to 90 or 315 to 45.
	 */
	public Arc(double begin, double end) {
		this.beginAngle = begin;
		this.endAngle = end;
	}
	
	public Arc(Arc another) {
		this.beginAngle = another.beginAngle;
		this.endAngle = another.endAngle;
	}
	
	public boolean equals(Object obj)
	{
		if (obj == null) {
			return false;
		}
		
		if (obj instanceof Arc == false) {
			return false;
		}
		
		Arc another;
		another = (Arc) obj;
		
		if (another.beginAngle != this.beginAngle || another.endAngle != this.endAngle) {
			return false;
		}
		return true;
	}
	
	/**
	 * Returns true iff the given angle falls in our arc.
	 */
	public boolean contains(double angle)
	{
		angle = Geometry.ensurePositive(angle);
		
		if (beginAngle < endAngle)
		{
			if (angle >= beginAngle && angle <= endAngle) {
				return true;
			}
			return false;
		}
		else
		{
			if (angle >= beginAngle || angle <= endAngle)
			{
				return true;
			}
			return false;
		}
	}
	
	/**
	 * returns true iff the given arc fully falls within our arc
	 * 
	 * @param another
	 * @return
	 */
	public boolean contains(Arc another) {
		if (this.beginAngle == 0 && this.endAngle == 0) {
			return true;
		}
		if (another.beginAngle == 0 && another.endAngle == 0) {
			return (this.beginAngle == 0 && this.endAngle == 0);
		}
		return (this.contains(another.beginAngle) && this.contains(another.endAngle));
	}
	
	/**
	 * Returns a new Arc that contains the angles that belong to both this arc and the given arc. 
	 * 
	 * @param another the with which we the the intersection
	 * @return
	 */
	public Arc intersect(Arc another)
	{
		if (this.contains(another)) {
			return new Arc (another);
		}
		if (another.contains(this)) {
			return new Arc (this);
		}
		
		// from now on we can assume that neither of the arcs are fully within the other arc
		if (this.contains(another.beginAngle)) {
			// we contain the beginning but not the end, so..
			return new Arc(another.beginAngle, this.endAngle);
		}
		else
		{
			// we don't contain the beginning, but do we contain the end?
			if (this.contains(another.endAngle))
			{
				return new Arc(this.beginAngle, another.endAngle);
			}
			// we contain neither the beginning nor the end
			return null;
		}
	}
	
	/**
	 * returns true iff intersection with given arc is not empty.
	 * 
	 * @param another
	 * @return
	 */
	public boolean overlaps(Arc another)
	{
		if (this.contains(another)) {
			return true;
		}
		if (another.contains(this)) {
			return true;
		}
		
		// from now on we can assume that neither of the arcs are fully within the other arc
		if (this.contains(another.beginAngle)) {
			// we contain the beginning but not the end, so..
			return true;
		}
		else
		{
			// we don't contain the beginning, but do we contain the end?
			if (this.contains(another.endAngle))
			{
				return true;
			}
			// we contain neither the beginning nor the end
			return false;
		}
	}
	
	/**
	 * Returns the combination of this arc and the given arc.
	 * The given arc MUST overlap with this arc.
	 * 
	 * 
	 * @param another an Arc that MUST overlap with this Arc.
	 * @return
	 */
	public Arc combine(Arc another)
	{
		double begin, end;
		if (this.contains(another.beginAngle))
		{
			begin = this.beginAngle;
			if (this.contains(another.endAngle))
			{
				end = this.endAngle;
			}
			else
			{
				end = another.endAngle;
			}
		}
		else if (this.contains(another.endAngle))
		{
			// and we know that this does not contain the another arcs beginAngle
			begin = another.beginAngle;
			end = this.endAngle;
		}
		else
		{
			// the arcs don't overlap
			return null;
		}
			
		return new Arc(begin, end);
	}
	
	public double getMiddleAngle()
	{
		if (beginAngle < endAngle)
		{
			return (endAngle + beginAngle)/2.0;
		}
		else
		{
			double d = endAngle + 360.0;
			return Geometry.ensurePositive((beginAngle + d)/2.0);
		}
	}
	
	public double beginAngle;
	public double endAngle;
}
